package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.*;
import cn.wolfcode.feign.AliPayFeignApi;
import cn.wolfcode.feign.IntegralFeignApi;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.PayLogMapper;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mapper.SeckillProductMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMQResult;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;


@Service
//@Transactional
public class OrderInfoSeviceImpl implements IOrderInfoService {
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PayLogMapper payLogMapper;
    @Autowired
    private RefundLogMapper refundLogMapper;

    @Autowired
    private SeckillProductMapper seckillProductMapper;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;      // 注入rocketMQTemplate发送消息

    @Autowired
    private AliPayFeignApi aliPayFeignApi;          // 注入alipay远程调用Feign

    @Autowired
    private IntegralFeignApi integralFeignApi;      // 注入integral积分远程调用Feign

    @Override
    public String doSeckill(int time, Long seckillId, Long phone) {
        // 减少真实库存(操作t_seckill_product)
//        seckillProductMapper.decrCount(seckillId);
        seckillProductMapper.decrStock(seckillId);      // 解决超卖问题
        // 创建订单,把订单数据保存到数据库中t_order_info
        String orderNo = createOrder(time, seckillId, phone);
        // 往redis中存放一个key,为了判断是否重复下单做铺垫
        redisTemplate.opsForSet().add(SeckillRedisKey.SECKILL_ORDER_SET.getRealKey(time + ""), phone + ":" + seckillId);
        return orderNo;     // 返回订单编号
    }

    private String createOrder(int time, Long seckillId, Long phone) {
        SeckillProductVo seckillProductVo = seckillProductService.find(time, seckillId);    // 查询单个VO
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setCreateDate(new Date());
        orderInfo.setDeliveryAddrId(1L);
        orderInfo.setIntergral(seckillProductVo.getIntergral());
        orderInfo.setProductCount(1);
        orderInfo.setProductId(seckillProductVo.getProductId());
        orderInfo.setProductImg(seckillProductVo.getProductImg());
        orderInfo.setProductName(seckillProductVo.getProductName());
        orderInfo.setProductPrice(seckillProductVo.getProductPrice());
        orderInfo.setSeckillDate(new Date());
        orderInfo.setSeckillId(seckillId);
        orderInfo.setSeckillPrice(seckillProductVo.getSeckillPrice());
        orderInfo.setSeckillTime(time);
        orderInfo.setUserId(phone);
        orderInfo.setOrderNo(IdGenerateUtil.get().nextId()+"");
        orderInfoMapper.insert(orderInfo);
        return orderInfo.getOrderNo();      // 返回订单编号
    }

    @Override
    public void timeOutcancel(OrderMQResult orderMQResult) {
        // 1.判断订单状态是否是未支付状态
        OrderInfo orderInfo = orderInfoMapper.find(orderMQResult.getOrderNo());
        if (orderInfo != null) {
            // 2.判断是否为支付状态
            if (orderInfo.getStatus().equals(OrderInfo.STATUS_ARREARAGE)) {
                // 3.回补真实库存
                seckillProductMapper.incrStock(orderMQResult.getSeckillId());
                // 4.回补预库存
                seckillProductService.replenishRedisStockCount(orderMQResult.getTime(),orderMQResult.getSeckillId());
                // 5.修改本地标识 (发送修改本地标识)
                rocketMQTemplate.sendOneWay(MQConstant.CANCEL_SECKILL_OVER_SIGE_TOPIC,orderMQResult.getSeckillId());
                // 6.修改订单状态 (修改为超时取消订单)
                orderInfoMapper.updateCancelStatus(orderMQResult.getOrderNo(),OrderInfo.STATUS_TIMEOUT);
            }
        }
    }

    @Override
    public OrderInfo find(String orderNo) {
//        OrderInfo orderInfo = orderInfoMapper.find(orderNo);  // 查询mysql中数据
        Object objJson = redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_ORDER_HASH.getRealKey(""), orderNo);
        if (!StringUtils.isEmpty(objJson+"")) {
            OrderInfo orderInfo = JSON.parseObject(objJson+"", OrderInfo.class);
            return orderInfo;
        }
        return null;
    }

    @Value("${pay.returnUrl}")
    private String returnUrl;   // nacos中配置
    @Value("${pay.notifyUrl}")
    private String notifyUrl;

    @Override
    public String payOnline(String orderNo, int type) {
        // 调用find方法获取redis中orderInfo的数据
        OrderInfo orderInfo = find(orderNo);

        PayVo payVo = new PayVo();  // 创建PayVo对象封装信息
        payVo.setOutTradeNo(orderNo);
        payVo.setTotalAmount(orderInfo.getSeckillPrice().toString());     // 付款金额
        payVo.setSubject(orderInfo.getProductName());
        payVo.setBody(orderInfo.getProductName());
        payVo.setReturnUrl(returnUrl);
        payVo.setNotifyUrl(notifyUrl);
        // 远程调用支付(Feign)
        Result<String> result = aliPayFeignApi.payOnline(payVo);
        if (result != null && !result.hasError()) {
            return result.getData();
        }
        return "";
    }

    @Override
    public void paySuccess(Map<String, String> params) {
        String orderNo = params.get("out_trade_no");
        OrderInfo orderInfo = find(orderNo);        // redis中查询数据
        // 插入流水数据
        PayLog payLog = new PayLog();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        payLog.setTradeNo(orderNo);                                     // 订单编号
        payLog.setNotifyTime(simpleDateFormat.format(new Date()));      // 订单修改数据
        payLog.setTotalAmount(orderInfo.getSeckillPrice().toString());  // 支付价格
        payLog.setPayType(OrderInfo.PAYTYPE_ONLINE);                    // 支付类型
        payLogMapper.insert(payLog);
        // 修改订单状态
        orderInfoMapper.changePayStatus(orderNo,OrderInfo.STATUS_ACCOUNT_PAID,OrderInfo.PAYTYPE_ONLINE);   // 修改支付状态

    }

    @Override
    public void refundOnline(OrderInfo orderInfo) {
        // 1.插入流水信息记录
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderInfo.getOrderNo());
        refundLog.setRefundAmount(orderInfo.getSeckillPrice().toString());
        refundLog.setRefundReason("图文不符!~");
        refundLog.setRefundType(OrderInfo.PAYTYPE_ONLINE);
        refundLog.setRefundTime(new Date());
        refundLogMapper.insert(refundLog);      // 插入数据库(t_refund_log)
        // 2.远程调用支付服务
        RefundVo refundVo = new RefundVo();     // 封装信息进RefundVo中
        refundVo.setOutTradeNo(orderInfo.getOrderNo());
        refundVo.setRefundAmount(orderInfo.getSeckillPrice().toString());
        refundVo.setRefundReason("图文不符!~");

        Result<Boolean> result = aliPayFeignApi.refundOnline(refundVo);
        // data:boolean 如果data 为true表示退款成功 如果为false表示退款失败
        if (result == null || result.hasError() || !result.getData()) {
            throw new BusinessException(SeckillCodeMsg.REFUND_ERROR);   // 抛出退款失败
        }

        // 3.修改订单状态为退款状态
        orderInfoMapper.changeRefundStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND);
        // 4.回补真实库存
        seckillProductMapper.incrStock(orderInfo.getSeckillId());
        // 5.回补预库存
        seckillProductService.replenishRedisStockCount(orderInfo.getSeckillTime(),orderInfo.getSeckillId());
        // 6.修改本地标识
        rocketMQTemplate.sendOneWay(MQConstant.CANCEL_SECKILL_OVER_SIGE_TOPIC,orderInfo.getSeckillId());
    }

    @Override
    @GlobalTransactional
    public void payIntegral(String orderNo, int type) {
        OrderInfo orderInfo = find(orderNo);    // 使用订单编号从redis中查询订单数据
        // 1.插入流水记录
        PayLog payLog = new PayLog();
        payLog.setTradeNo(orderNo);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        payLog.setNotifyTime(simpleDateFormat.format(new Date()));
        payLog.setTotalAmount(orderInfo.getSeckillPrice().toString());
        payLog.setPayType(type);
        payLogMapper.insert(payLog);            // 插入数据库
        // 2.远程调用积分服务减少用户积分
        OperateIntegralVo operateIntegralVo = new OperateIntegralVo();      // 创建OperateIntegralVo对象封装信息
        operateIntegralVo.setPk(orderNo);
        operateIntegralVo.setValue(orderInfo.getIntergral());
        operateIntegralVo.setInfo(orderInfo.getProductName());
        operateIntegralVo.setUserId(orderInfo.getUserId());
        Result<String> result = integralFeignApi.decrIntegral(operateIntegralVo);
        if (result == null || result.hasError()) {
            if (result.getMsg().equals("操作积分失败")) {
                throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
            } else {
                CodeMsg codeMsg = new CodeMsg();
                codeMsg.setCode(result.getCode());
                codeMsg.setMsg(result.getMsg());
                throw new BusinessException(codeMsg);
            }
        }
        // 3.修改订单状态为已支付状态
        // 返回值为影响行
        int count = orderInfoMapper.changePayStatus(orderNo, OrderInfo.STATUS_ACCOUNT_PAID, OrderInfo.PAYTYPE_INTERGRAL);
        if (count == 0) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
        }
    }

    @Override
    @GlobalTransactional
    public void refundIntegral(OrderInfo orderInfo) {
        // 1.插入流水记录
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderInfo.getOrderNo());
        refundLog.setRefundAmount(orderInfo.getIntergral().toString());
        refundLog.setRefundReason("图文不符,要不起~");
        refundLog.setRefundType(OrderInfo.PAYTYPE_INTERGRAL);
        refundLog.setRefundTime(new Date());
        refundLogMapper.insert(refundLog);
        // 2.远程调用积分服务进行加用户积分
        OperateIntegralVo operateIntegralVo = new OperateIntegralVo();
        operateIntegralVo.setPk(orderInfo.getOrderNo());
        operateIntegralVo.setValue(orderInfo.getIntergral());
        operateIntegralVo.setInfo(orderInfo.getProductName());
        operateIntegralVo.setUserId(orderInfo.getUserId());
        Result<String> result = integralFeignApi.incrIntegral(operateIntegralVo);
        if (result == null || result.hasError()) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
        }
//        int i = 1 / 0;    // 假设退款流程中抛出异常进行分布式事务回滚
        // 3.修改订单状态为退款状态
        int count = orderInfoMapper.changeRefundStatus(orderInfo.getOrderNo(), OrderInfo.STATUS_REFUND);
        if (count == 0) {
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);
        }
        // 4.回补真实库存
        seckillProductMapper.incrStock(orderInfo.getSeckillId());
        // 5.回补预库存
        seckillProductService.replenishRedisStockCount(orderInfo.getSeckillTime(),orderInfo.getSeckillId());
        // 6.修改本地标识 (取消本地标识)
        rocketMQTemplate.sendOneWay(MQConstant.CANCEL_SECKILL_OVER_SIGE_TOPIC,orderInfo.getSeckillId());
    }
}
